# -*-coding:utf-8-*-

import logging
import time
import tornado
from tornado.web import asynchronous
from tornado.httpclient import AsyncHTTPClient, HTTPRequest, HTTPClient
from tornado.gen import coroutine, Return
from tornado.options import define, options
import tests.pyrestful.rest
from tests.pyrestful.rest import post, get, mediatypes

define('port', default=18608, help='Run service on a specific port', type=int)
define('debug', help='Use debug settings for test purpose', type=bool)
options.parse_command_line()


class AsyncTestHandler(tests.pyrestful.rest.RestHandler):
    def __init__(self, application, request, **kwargs):
        super(AsyncTestHandler, self).__init__(application, request, **kwargs)

    def normal_func(self):
        pass

    # NOTE: 在单线程情况下，异步，异步，再同步，OK！最后面的同步会先执行
    # @asynchronous
    @coroutine
    @get(_path='/test/get/result/async_call_async', _produces=mediatypes.APPLICATION_JSON)
    def get_result_async_call_async(self):
        url = '%s://%s/test/get/result' % (self.request.protocol, self.request.host)
        http_request = HTTPRequest(url, method='GET')
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch(http_request, raise_error=False)
        self.finish(response.body)

    # NOTE: 在单线程情况下，同步调用异步，阻塞！
    @get(_path='/test/get/result/sync_call_async', _produces=mediatypes.APPLICATION_JSON)
    def get_result_sync_call_async(self):
        url = '%s://%s/test/get/result' % (self.request.protocol, self.request.host)
        http_request = HTTPRequest(url, method='GET')
        http_client = HTTPClient()
        response = http_client.fetch(http_request, raise_error=False)
        self.finish(response.body)

    # NOTE: http handler 中使用 coroutine 必须配合 asynchronous 使用，而且顺序必须是 asynchronous 先于 coroutine
    # @asynchronous
    @coroutine
    @get(_path='/test/get/result', _produces=mediatypes.APPLICATION_JSON)
    def get_result(self):
        self.normal_func()
        # NOTE: 函数必须使用 coroutine 修饰。如果需要等待其返回的话，需要使用 yield
        # DONE: 如何在框架级别处理异步执行时抛异常的情况?
        # 解决方案：将 pyrestful package 集成在项目中，修改 rest.py，将 get/post 等方法用 coroutine 封装...
        # try:
        #     response = yield self.do_get_result()
        # except Exception as e:
        #     print str(e)
        response = yield self.do_get_result()
        self.finish(response.body)
        # NOTE: launch coroutine in ASYNC context -> OK
        # self.do_get_result()

    @coroutine
    @get(_path='/test/get/result/exc', _produces=mediatypes.APPLICATION_JSON)
    def get_result_exc(self):
        # 1 / 0
        raise Exception('exc test')

    def gen_http_error(self, status, msg):
        import traceback
        self.finish(traceback.format_exc())

    @coroutine
    def do_get_result(self):
        # 1 / 0
        url = '%s://%s/test/do/some/things' % (self.request.protocol, self.request.host)
        http_request = HTTPRequest(url, method='POST', body='')
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch(http_request, raise_error=False)
        # Return: Special exception to return a value from a `coroutine`
        raise Return(response)

    @get(_path='/test/get/result/sync', _produces=mediatypes.APPLICATION_JSON)
    def get_result_sync(self):
        url = '%s://%s/test/do/some/things' % (self.request.protocol, self.request.host)
        http_request = HTTPRequest(url, method='POST', body='')
        http_client = HTTPClient()
        response = http_client.fetch(http_request, raise_error=False)
        self.finish(response.body)

    @get(_path='/test/get/result/sync/succ', _produces=mediatypes.APPLICATION_JSON, _manual_response=True)
    def get_result_sync_succ(self):
        self.finish({'say': '[%f] hello async...' % time.time()})
        # launch coroutine in SYNC context -> OK
        self.do_get_result()

    @post(_path='/test/do/some/things', _produces=mediatypes.APPLICATION_JSON)
    def do_some_things(self):
        self.finish({'say': '[%f] hello async...' % time.time()})


def main():
    logger = logging.getLogger()
    application = tests.pyrestful.rest.RestService([AsyncTestHandler])
    # To get remote_ip, it should set xheaders=True when initializing HTTPServer
    application.listen(options.port, xheaders=True)
    logger.info('api server start at %d' % options.port)
    tornado.ioloop.IOLoop.instance().start()


if __name__ == '__main__':
    main()
